/*
 * Copyright (c) 1993 Daniel Boulet
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that this entire comment appears intact.
 *
 * Redistribution in binary form may occur without any restrictions.
 * Obviously, it would be nice if you gave credit where credit is due
 * but requiring it would be too onerous.
 *
 * This software is provided ``AS IS'' without any warranties of any kind.
 *
 */

/*
 *  Linux port (c) 1994 Bob Beck
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that this entire comment appears intact.
 *
 * This software is provided ``AS IS'' without any warranties of any kind.
 *
 */

/*
 *	Drastically cleaned up: Alan Cox Dec 1st 1994.
 */

/*
 * Command line interface for IP firewall facility
 */

/* $Header: /usr/brule/dept/beck/ipfirewall/RCS/ipfirewall.c,v 1.1 1994/07/28 00:06:11 beck Exp $ */
/* $Log: ipfirewall.c,v $
 * Revision 1.1  1994/07/28  00:06:11  beck
 * Initial revision
 * */


#include <sys/types.h>
#ifdef _LINUX_TYPES_H
/* Yep. it's Linux */
#ifndef LINUX
#define LINUX
#endif
#endif
#include <sys/socket.h>
#define IPFIREWALL
#include <netinet/in.h>
#include <netinet/in_system.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifndef LINUX
#include <netinet/ip_fw.h>
#else
#include <linux/ip_fw.h>	/* Until it gets into stdinc */
#endif
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef LINUX
#include <ctype.h>
#define IPVERSION 4
#endif

typedef enum
{
	IPF_BLOCKING = 0,
	IPF_FORWARDING = 1,
	IPF_ACCOUNTING = 2
} ipf_kind;

static char *ipf_names[3] =
	{"blocking", "forwarding", "accounting"};
static long ipf_addfunc[3] =
	{IP_FW_ADD_BLK, IP_FW_ADD_FWD, IP_ACCT_ADD};
static long ipf_delfunc[3] =
	{IP_FW_DEL_BLK, IP_FW_DEL_FWD, IP_ACCT_DEL};


void
show_usage ()
{
	fprintf (stderr, "ipfirewall:  {\n");
	fprintf (stderr, "               list ...\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               flush\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               checkb[locking] ...\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               checkf[orwarding] ...\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               addb[lock] ...\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               addf[orwarding] ...\n");
	fprintf (stderr, "             }\n");
	fprintf (stderr, "               adda[ccounting] ...\n");
	fprintf (stderr, "             }\n");
}

/*
 * I'm not sure that this is practical ...
 */

void
show_help ()
{
	abort ();
	fprintf (stderr, "ipfirewall:  {\n");
	fprintf (stderr, "               list\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               flush\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               checkb[locking] <type> <src> <dest>\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               checkf[orwarding] <type> <src> <dest>\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               addb[lock] <type> <src> <dest>\n");
	fprintf (stderr, "             |\n");
	fprintf (stderr, "               addf[orwarding] <type> <src> <dest>\n");
	fprintf (stderr, "             }\n");
	fprintf (stderr, "               adda[ccounting] <type> <src> <dest>\n");
	fprintf (stderr, "             }\n");
	fprintf (stderr, "where\n");
	fprintf (stderr, "       <src> ::= <host>:<port> /* for TCP or UDP */\n");
	fprintf (stderr, "       <src> ::= <host>        /* for ICMP */\n");
	fprintf (stderr, "      <host> ::= <byte>.<byte>.<byte>.<byte> | <hostname>\n");
	fprintf (stderr, "      <port> ::= <short> | <servicename>\n");
	fprintf (stderr, "     <short> ::= an integer in the range 1-65535\n");
	fprintf (stderr, "      <byte> ::= an integer in the range 0-255\n");
}

static
char *
fmtip (u_long uaddr)
{
	static char tbuf[100];

	sprintf (tbuf, "%d.%d.%d.%d",
		 ((char *) &uaddr)[0] & 0xff,
		 ((char *) &uaddr)[1] & 0xff,
		 ((char *) &uaddr)[2] & 0xff,
		 ((char *) &uaddr)[3] & 0xff);

	return (&tbuf[0]);
}

static
void
print_ports (int cnt, int range, u_short * ports)
{
	int ix;
	char *pad;

	if (range)
	{
		if (cnt < 2)
		{
			fprintf (stderr, "ipfirewall:  range flag set but only %d ports\n", cnt);
			abort ();
		}
		printf ("%d:%d", ports[0], ports[1]);
		ix = 2;
		pad = " ";
	}
	else
	{
		ix = 0;
		pad = "";
	}

	while (ix < cnt)
	{
		printf ("%s%d", pad, ports[ix]);
		pad = " ";
		ix += 1;
	}
}

int
do_setsockopt (char *what, int fd, int proto, int cmd, void *data, int datalen, int ok_errno)
{
	char *cmdname;

	switch (cmd)
	{
	case IP_FW_FLUSH:
		cmdname = "IP_FW_FLUSH";
		break;
	case IP_FW_CHK_BLK:
		cmdname = "IP_FW_CHK_BLK";
		break;
	case IP_FW_CHK_FWD:
		cmdname = "IP_FW_CHK_FWD";
		break;
	case IP_FW_ADD_BLK:
		cmdname = "IP_FW_ADD_BLK";
		break;
	case IP_FW_ADD_FWD:
		cmdname = "IP_FW_ADD_FWD";
		break;
	case IP_FW_DEL_BLK:
		cmdname = "IP_FW_DEL_BLK";
		break;
	case IP_FW_DEL_FWD:
		cmdname = "IP_FW_DEL_FWD";
		break;
	case IP_ACCT_ADD:
		cmdname = "IP_ACCT_ADD";
		break;
	case IP_ACCT_DEL:
		cmdname = "IP_ACCT_DEL";
		break;
	default:
		fprintf (stderr, "ipfirewall:  unknown command (%d) passed to do_setsockopt - bye!\n", cmd);
		abort ();
	}

	if (fd < 0)
	{
		printf ("setsockopt(%d, %d, %s, 0x%x, 0x%x)\n", fd, proto, cmdname, (int) data, datalen);
		if (cmd == IP_FW_CHK_BLK || cmd == IP_FW_CHK_FWD)
		{
			struct iphdr *ip = (struct iphdr *) data;
			struct tcphdr *tcp = (struct tcphdr *) &(((int *) ip)[ip->ihl]);
			if (ip->ihl != sizeof (struct iphdr) / sizeof (int))
			{
				fprintf (stderr, "ip header length %d, should be %d\n", ip->ihl, sizeof (struct iphdr) / sizeof (int));
			}
			if (ip->protocol != IPPROTO_TCP && ip->protocol != IPPROTO_UDP)
				abort ();
			printf ("data = struct iphdr : struct %shdr {\n", ip->protocol == IPPROTO_TCP ? "tcp" : "udp");
			printf ("\tsrc=%s ", fmtip (ip->saddr));
			printf ("%d\n", ntohs (tcp->th_sport));
			printf ("\tdst=%s  ", fmtip (ip->daddr));
			printf ("%d\n", ntohs (tcp->th_dport));
			printf ("}\n");
		}
		else if (cmd == IP_FW_ADD_BLK || cmd == IP_FW_ADD_FWD || cmd == IP_ACCT_ADD)
		{
			struct ip_fw *fp = (struct ip_fw *) data;
			int fmt_ports;
			printf ("data = struct ip_fw {\n");
			if (fp->flags & IP_FW_F_ACCEPT)
			{
				printf ("\taccept ");
			}
			else
			{
				printf ("\tdeny ");
			}
			switch (fp->flags & IP_FW_F_KIND)
			{
			case IP_FW_F_ALL:
				printf ("\tuniversal\n");
				fmt_ports = 0;
				break;
			case IP_FW_F_TCP:
				printf ("tcp\n");
				fmt_ports = 1;
				break;
			case IP_FW_F_UDP:
				printf ("udp\n");
				fmt_ports = 1;
				break;
			case IP_FW_F_ICMP:
				printf ("icmp\n");
				fmt_ports = 0;
				break;
			}
			printf ("\tsrc=%s:", fmtip (fp->src.s_addr));
			printf ("%s ", fmtip (fp->src_mask.s_addr));
			if (fmt_ports)
			{
				print_ports (fp->n_src_p, fp->flags & IP_FW_F_SRNG, &fp->ports[0]);
			}
			else if (fp->flags & (IP_FW_F_SRNG | IP_FW_F_DRNG))
			{
				abort ();
			}
			else if (fp->n_src_p > 0 || fp->n_dst_p > 0)
			{
				abort ();
			}
			printf ("\n");
			printf ("\tdst=%s:", fmtip (fp->dst.s_addr));
			printf ("%s ", fmtip (fp->dst_mask.s_addr));
			if (fmt_ports)
			{
				print_ports (fp->n_dst_p, fp->flags & IP_FW_F_DRNG, &fp->ports[fp->n_src_p]);
			}
			printf ("\n");
			printf ("}\n");
		}
	}
	else
	{
		if (setsockopt (fd, proto, cmd, data, datalen) < 0)
		{
			if (errno == ok_errno)
			{
				return (errno);
			}
			perror ("ipfirewall:  setsockopt");
			exit (1);
		}
	}
	return (0);
}

void
show_parms (char **argv)
{
	while (*argv)
	{
		printf ("%s ", *argv++);
	}
}

int
get_protocol (char *arg, void (*cmd_usage) (ipf_kind), ipf_kind kind)
{
	if (arg == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing protocol name\n");
	}
	else if (strcmp (arg, "tcp") == 0)
	{
		return (IP_FW_F_TCP);
	}
	else if (strcmp (arg, "udp") == 0)
	{
		return (IP_FW_F_UDP);
	}
	else if (strcmp (arg, "icmp") == 0)
	{
		return (IP_FW_F_ICMP);
	}
	else if (strcmp (arg, "all") == 0)
	{
		return (IP_FW_F_ALL);
	}
	else
	{
		fprintf (stderr, "illegal protocol name \"%s\"\n", arg);
	}
	(*cmd_usage) (kind);
	exit (1);
	return (0);
}

void
get_ipaddr (char *arg, struct in_addr *addr, struct in_addr *mask, void (*usage) (ipf_kind), ipf_kind kind)
{
	char *p, *tbuf;
	int period_cnt, non_digit;
	struct hostent *hptr;

	if (arg == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing ip address\n");
		exit (1);
	}

	period_cnt = 0;
	non_digit = 0;
	for (p = arg; *p != '\0' && *p != '/' && *p != ':'; p += 1)
	{
		if (*p == '.')
		{
			if (p > arg && *(p - 1) == '.')
			{
				fprintf (stderr, "ipfirewall:  two periods in a row in ip address (%s)\n", arg);
				exit (1);
			}
			period_cnt += 1;
		}
		else if (!isdigit (*p))
		{
			non_digit = 1;
		}
	}

	tbuf = malloc (p - arg + 1);
	strncpy (tbuf, arg, p - arg);
	tbuf[p - arg] = '\0';

	if (non_digit)
	{

		hptr = gethostbyname (tbuf);
		if (hptr == NULL)
		{
			fprintf (stderr, "ipfirewall:  unknown host \"%s\"\n", tbuf);
			exit (1);
		}
		if (hptr->h_length != sizeof (struct in_addr))
		{
			fprintf (stderr, "ipfirewall:  hostentry addr length = %d, expected %d (i.e. sizeof(struct in_addr))\n",
				 hptr->h_length, sizeof (struct in_addr));
			exit (1);
		}

		bcopy (hptr->h_addr, addr, sizeof (struct in_addr));

	}
	else
	{

		if (period_cnt == 3)
		{

			int a1, a2, a3, a4, matched;

			if ((matched = sscanf (tbuf, "%d.%d.%d.%d", &a1, &a2, &a3, &a4))
			    != 4)
			{
				fprintf (stderr,
					 "ipfirewall: Only %d fields matched in IP address!\n",
					 matched);
				/* should this exit here? or catch it later? -BB */
			}

			if (a1 > 255 || a2 > 255 || a3 > 255 || a4 > 255)
			{
				fprintf (stderr, "ipfirewall:  number too large in ip address (%s)\n", arg);
				exit (1);
			}

			((char *) addr)[0] = a1;
			((char *) addr)[1] = a2;
			((char *) addr)[2] = a3;
			((char *) addr)[3] = a4;

		}
		else if (strcmp (tbuf, "0") == 0)
		{

			((char *) addr)[0] = 0;
			((char *) addr)[1] = 0;
			((char *) addr)[2] = 0;
			((char *) addr)[3] = 0;

		}
		else
		{

			fprintf (stderr, "ipfirewall:  incorrect ip address format \"%s\" (expected 3 periods)\n", tbuf);
			exit (1);

		}

	}

	free (tbuf);

	if (mask == NULL)
	{

		if (*p != '\0')
		{
			fprintf (stderr, "ipfirewall:  ip netmask not allowed here (%s)\n", (char *) addr);
			exit (1);
		}

	}
	else
	{

		if (*p == ':')
		{

			get_ipaddr (p + 1, mask, NULL, usage, kind);

		}
		else if (*p == '/')
		{

			int bits;
			char *end;

			p += 1;
			if (*p == '\0')
			{
				fprintf (stderr, "ipfirewall:  missing mask value (%s)\n", arg);
				exit (1);
			}
			else if (!isdigit (*p))
			{
				fprintf (stderr, "ipfirewall:  non-numeric mask value (%s)\n", arg);
				exit (1);
			}

			bits = strtol (p, &end, 10);
			if (*end != '\0')
			{
				fprintf (stderr, "ipfirewall:  junk after mask (%s)\n", arg);
				exit (1);
			}

			if (bits < 0 || bits > sizeof (u_long) * 8)
			{
				fprintf (stderr, "ipfirewall:  mask length value out of range (%s)\n", arg);
				exit (1);
			}

			if (bits == 0)
			{	/* left shifts of 32 aren't defined */
				mask->s_addr = 0;
			}
			else
			{
				((char *) mask)[0] = (-1 << (32 - bits)) >> 24;
				((char *) mask)[1] = (-1 << (32 - bits)) >> 16;
				((char *) mask)[2] = (-1 << (32 - bits)) >> 8;
				((char *) mask)[3] = (-1 << (32 - bits)) >> 0;
			}

		}
		else if (*p == '\0')
		{
			mask->s_addr = 0xffffffff;
		}
		else
		{

			fprintf (stderr, "ipfirewall:  junk after ip address (%s)\n", arg);
			exit (1);

		}

		/*
	 * Mask off any bits in the address that are zero in the mask.
	 * This allows the user to describe a network by specifying
	 * any host on the network masked with the network's netmask.
	 */
		addr->s_addr &= mask->s_addr;

	}

}

u_short
get_one_port (char *arg, void (*usage) (ipf_kind), ipf_kind kind, const char *proto_name)
{
	int slen = strlen (arg);

	if (slen > 0 && strspn (arg, "0123456789") == slen)
	{
		int port;
		char *end;

		port = strtol (arg, &end, 10);
		if (*end != '\0')
		{
			fprintf (stderr, "ipfirewall:  illegal port number (%s)\n", arg);
			exit (1);
		}

		if (port <= 0 || port > 65535)
		{
			fprintf (stderr, "ipfirewall:  port number out of range (%d)\n", port);
			exit (1);
		}

		return (port);

	}
	else
	{

		struct servent *sptr;

		sptr = getservbyname (arg, proto_name);

		if (sptr == NULL)
		{
			fprintf (stderr, "ipfirewall:  unknown %s service \"%s\"\n", proto_name, arg);
			exit (1);
		}

		return (ntohs (sptr->s_port));

	}

}

int
get_ports (char ***argv_ptr, u_short * ports, int min_ports, int max_ports, void (*usage) (ipf_kind), ipf_kind kind, const char *proto_name)
{
	int ix;
	char *arg;
	int sign;

	ix = 0;
	sign = 1;
	while ((arg = **argv_ptr) != NULL && strcmp (arg, "from") != 0 && strcmp (arg, "to") != 0)
	{

		char *p;

	/*
	 * Check that we havn't found too many port numbers.
	 * We do this here instead of with another condition on the while loop
	 * so that the caller can assume that the next parameter is NOT a port number.
	 */

		if (ix >= max_ports)
		{
			fprintf (stderr, "ipfirewall:  too many port numbers (max %d, got at least %d, next parm=\"%s\")\n", max_ports, max_ports + 1, arg);
			exit (1);
		}

		if ((p = strchr (arg, ':')) == NULL)
		{

			ports[ix++] = get_one_port (arg, usage, kind, proto_name);

		}
		else
		{

			if (ix > 0)
			{

				fprintf (stderr, "ipfirewall:  port ranges are only allowed for the first port value pair (%s)\n", arg);
				exit (1);

			}

			if (max_ports > 1)
			{

				char *tbuf;

				tbuf = malloc ((p - arg) + 1);
				strncpy (tbuf, arg, p - arg);
				tbuf[p - arg] = '\0';

				ports[ix++] = get_one_port (tbuf, usage, kind, proto_name);
				ports[ix++] = get_one_port (p + 1, usage, kind, proto_name);
				sign = -1;

			}
			else
			{

				fprintf (stderr, "ipfirewall:  port range not allowed here (%s)\n", arg);
				exit (1);

			}
		}

		*argv_ptr += 1;
	}

	if (ix < min_ports)
	{
		if (min_ports == 1)
		{
			fprintf (stderr, "ipfirewall:  missing port number%s\n", max_ports == 1 ? "" : "(s)");
		}
		else
		{
			fprintf (stderr, "ipfirewall:  not enough port numbers (expected %d, got %d)\n", min_ports, ix);
		}
		exit (1);
	}

	return (sign * ix);

}

void
check_usage (ipf_kind kind)
{
	fprintf (stderr, "usage:  ipfirewall check%s ...\n", ipf_names[kind]);
}

void
check (ipf_kind kind, int socket_fd, char **argv)
{
	int protocol;
	struct iphdr *packet;
	char *proto_name;

	packet = (struct iphdr *) malloc (sizeof (struct iphdr) + sizeof (struct tcphdr));
	packet->version = IPVERSION;
	packet->ihl = sizeof (struct iphdr) / sizeof (int);
	printf ("check%s ", kind == IPF_BLOCKING ? "blocking" : "forwarding");
	show_parms (argv);
	printf ("\n");

	proto_name = *argv++;
	protocol = get_protocol (proto_name, check_usage, kind);
	switch (protocol)
	{
	case IP_FW_F_TCP:
		packet->protocol = IPPROTO_TCP;
		break;
	case IP_FW_F_UDP:
		packet->protocol = IPPROTO_UDP;
		break;
	default:
		fprintf (stderr, "ipfirewall:  can only check TCP or UDP packets\n");
		break;
	}

	if (*argv == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing \"from\" from keyword\n");
		exit (1);
	}
	if (strcmp (*argv, "from") == 0)
	{
		argv += 1;
		get_ipaddr (*argv++, (struct in_addr *) &packet->saddr, NULL, check_usage, kind);
		if (protocol == IP_FW_F_TCP || protocol == IP_FW_F_UDP)
		{
			get_ports (&argv, &((struct tcphdr *) (&packet[1]))->th_sport, 1, 1, check_usage, kind, proto_name);
			((struct tcphdr *) (&packet[1]))->th_sport = htons (
				  ((struct tcphdr *) (&packet[1]))->th_sport
				);
		}
	}
	else
	{
		fprintf (stderr, "ipfirewall:  expected \"from\" keyword, got \"%s\"\n", *argv);
		exit (1);
	}

	if (*argv == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing \"to\" from keyword\n");
		exit (1);
	}
	if (strcmp (*argv, "to") == 0)
	{
		argv += 1;
		get_ipaddr (*argv++, (struct in_addr *) &packet->daddr, NULL, check_usage, kind);
		if (protocol == IP_FW_F_TCP || protocol == IP_FW_F_UDP)
		{
			get_ports (&argv, &((struct tcphdr *) (&packet[1]))->th_dport, 1, 1, check_usage, kind, proto_name);
			((struct tcphdr *) (&packet[1]))->th_dport = htons (
				  ((struct tcphdr *) (&packet[1]))->th_dport
				);
		}
	}
	else
	{
		fprintf (stderr, "ipfirewall:  expected \"to\" keyword, got \"%s\"\n", *argv);
		exit (1);
	}

	if (*argv == NULL)
	{

		if (do_setsockopt (kind == IPF_BLOCKING ? "checkblocking" : "checkforwarding",
				   socket_fd, IPPROTO_IP,
		       kind == IPF_BLOCKING ? IP_FW_CHK_BLK : IP_FW_CHK_FWD,
				   packet,
			     sizeof (struct iphdr) + sizeof (struct tcphdr),
				   EACCES
		    ) == 0
		)
		{
			printf ("packet accepted by %s firewall\n",
			  kind == IPF_BLOCKING ? "blocking" : "forwarding");
		}
		else
		{
			printf ("packet rejected by %s firewall\n",
			  kind == IPF_BLOCKING ? "blocking" : "forwarding");
		}

		return;

	}
	else
	{
		fprintf (stderr, "ipfirewall:  extra parameters at end of command (");
		show_parms (argv);
		fprintf (stderr, ")\n");
		exit (1);
	}
}

void
add_usage (ipf_kind kind)
{
	fprintf (stderr, "usage:  ipfirewall add%s ...\n", ipf_names[kind]);
}

void
add (ipf_kind kind, int socket_fd, char **argv)
{
	int protocol, accept_firewall, src_range, dst_range;
	struct ip_fw firewall;
	char *proto_name;

	printf ("add%s ", ipf_names[kind]);
	show_parms (argv);
	printf ("\n");

	if (kind != IPF_ACCOUNTING)
	{

		if (*argv == NULL)
		{
			fprintf (stderr, "ipfirewall:  missing \"accept\" or \"deny\" keyword\n");
			exit (1);
		}

		if (strcmp (*argv, "deny") == 0)
		{
			accept_firewall = 0;
		}
		else if (strcmp (*argv, "accept") == 0)
		{
			accept_firewall = IP_FW_F_ACCEPT;
		}
		else
		{
			fprintf (stderr, "ipfirewall:  expected \"accept\" or \"deny\", got \"%s\"\n", *argv);
			exit (1);
		}

		argv += 1;
	}
	else
		accept_firewall = 0;
	proto_name = *argv++;
	protocol = get_protocol (proto_name, add_usage, kind);

	if (*argv == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing \"from\" keyword\n");
		exit (1);
	}
	if (strcmp (*argv, "from") == 0)
	{
		argv++;
		get_ipaddr (*argv++, &firewall.src, &firewall.src_mask, add_usage, kind);
		if (protocol == IP_FW_F_TCP || protocol == IP_FW_F_UDP)
		{
			int cnt;
			cnt = get_ports (&argv, &firewall.ports[0], 0, IP_FW_MAX_PORTS, add_usage, kind, proto_name);
			if (cnt < 0)
			{
				src_range = IP_FW_F_SRNG;
				cnt = -cnt;
			}
			else
			{
				src_range = 0;
			}
			firewall.n_src_p = cnt;
		}
		else
		{
			firewall.n_src_p = 0;
			src_range = 0;
		}
	}
	else
	{
		fprintf (stderr, "ipfirewall:  expected \"from\", got \"%s\"\n", *argv);
		exit (1);
	}

	if (*argv == NULL)
	{
		fprintf (stderr, "ipfirewall:  missing \"to\" keyword\n");
		exit (1);
	}
	if (strcmp (*argv, "to") == 0)
	{
		argv++;
		get_ipaddr (*argv++, &firewall.dst, &firewall.dst_mask, add_usage, kind);
		if (protocol == IP_FW_F_TCP || protocol == IP_FW_F_UDP)
		{
			int cnt;
			cnt = get_ports (&argv, &firewall.ports[firewall.n_src_p], 0, IP_FW_MAX_PORTS - firewall.n_src_p, add_usage, kind, proto_name);
			if (cnt < 0)
			{
				dst_range = IP_FW_F_DRNG;
				cnt = -cnt;
			}
			else
			{
				dst_range = 0;
			}
			firewall.n_dst_p = cnt;
		}
		else
		{
			firewall.n_dst_p = 0;
			dst_range = 0;
		}
	}
	else
	{
		fprintf (stderr, "ipfirewall:  expected \"to\", got \"%s\"\n", *argv);
		exit (1);
	}

	if (*argv == NULL)
	{

		firewall.flags = protocol | accept_firewall | src_range | dst_range;
		(void) do_setsockopt (ipf_names[kind],
				      socket_fd, IPPROTO_IP,
				      ipf_addfunc[kind],
				      &firewall,
				      sizeof (firewall),
				      0
			);

	}
	else
	{
		fprintf (stderr, "ipfirewall:  extra parameters at end of command (");
		show_parms (argv);
		fprintf (stderr, ")\n");
		exit (1);
	}
}

static int count_mask(unsigned long mask)
{
	int ct;
	for(ct=0;(mask&0x80000000);ct++)
		mask<<=1;
	return ct;
}

void list_file(char *path, int acct)
{
	FILE * f=fopen(path,"r");
	char buf[256];
	unsigned long sa,da,sm,dm;
	unsigned short nsp, ndp;
	unsigned long npkt, nbyt;
	unsigned int ports[10];
	int flags;
	if(f==NULL)
	{
		perror(path);
		exit(1);
	}
	fgets(buf,255,f);	/* Title */
	while(fgets(buf,255,f))
	{
		unsigned int *pp=&ports[0];
		sscanf(buf,"%lX/%lX->%lX/%lX %X %u %u %lu %lu %u %u %u %u %u %u %u %u %u %u",
			&sa,&sm,&da,&dm,&flags, &nsp, &ndp, &npkt, &nbyt,
			&ports[0],&ports[1],&ports[2],&ports[3],&ports[4],
			&ports[5],&ports[6],&ports[7],&ports[8],&ports[9]);
		
		if(!acct)
		{
			if(flags&IP_FW_F_ACCEPT)
				printf("Accept ");
			else
				printf("Reject ");
		}
		else
			printf("%lu packets (%lu bytes) matching: ",
				npkt,nbyt);
				
		if(flags&IP_FW_F_ALL)
			printf("all  from ");
		else if(flags&IP_FW_F_TCP)
			printf("TCP  from ");
		else if(flags&IP_FW_F_UDP)
			printf("UDP  from ");
		else
			printf("ICMP from ");
		if(sm==0)
			printf("anywhere to ");
		else
			printf("%s/%d to ",fmtip(htonl(sa)),count_mask(sm));
		if(dm==0)
			printf("anywhere");
		else
			printf("%s/%d", fmtip(htonl(da)),count_mask(dm));
		
		if(flags&(IP_FW_F_TCP|IP_FW_F_UDP))
		{
			printf("  Ports: ");
			if(nsp==0)
				printf("any ");
			else
			{
				if(flags&IP_FW_F_SRNG)
				{	
					printf("%u-%u ",pp[0],pp[1]);
					pp+=2;
					nsp-=2;
				}
				while(nsp-->0)
					printf("%u ",*pp++);
			}
			printf("to");
		
			if(ndp==0)
				printf(" any");
			else
			{
				if(flags&IP_FW_F_DRNG)
				{
					printf(" %u-%u",pp[0],pp[1]);
					pp+=2;
				}
				while(ndp-->0)
					printf(" %u",*pp++);		
			}
		}
		printf("\n");
	}
	fclose(f);
}

void list(int socket_fd, char **argv)
{
	if(*argv==NULL && **argv)
	{
		fprintf(stderr,"blocking, forwarding or accounting keyword expected.\n");
		exit(1);
	}
	if(strncmp(*argv,"blocking",strlen(*argv))==0)
	{
		list_file("/proc/net/ip_block",0);
		return;
	}
	if(strncmp(*argv,"forwarding",strlen(*argv))==0)
	{
		list_file("/proc/net/ip_forward",0);
		return;
	}
	if(strncmp(*argv,"accounting",strlen(*argv))==0)
	{
		list_file("/proc/net/ip_acct",1);
		return;
	}
	fprintf(stderr,"Found '%s': 'blocking', 'forwarding' or 'accounting' keyword expected.\n",*argv);
	exit(1);
}
		
void
main (argc, argv)
     int argc;
     char **argv;
{
	int socket_fd;

	socket_fd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);

	if (socket_fd < 0)
	{
		perror ("ipfirewall:");
		exit(1);
	}

	if (argc == 1)
	{
		show_usage ();
		exit (1);
	}

	if (strcmp (argv[1], "list") == 0)
	{
		list(socket_fd, &argv[2]);
	}
	else if (strcmp (argv[1], "flush") == 0)
	{
#ifdef LINUX
		/* Same kludge as above, see above ranting and griping -BB */
		unsigned long fred = 1;
		(void) do_setsockopt (argv[1], socket_fd, IPPROTO_IP, IP_FW_FLUSH, &fred, sizeof (unsigned long), 0);
#else
		(void) do_setsockopt (argv[1], socket_fd, IPPROTO_IP, IP_FLUSH_FIREWALLS, NULL, 0, 0);
#endif

	}
	else if (strlen (argv[1]) >= strlen ("checkb")
	       && strncmp (argv[1], "checkblocking", strlen (argv[1])) == 0)
	{

		check (IPF_BLOCKING, socket_fd, &argv[2]);

	}
	else if (strlen (argv[1]) >= strlen ("checkf")
	     && strncmp (argv[1], "checkforwarding", strlen (argv[1])) == 0)
	{

		check (IPF_FORWARDING, socket_fd, &argv[2]);

	}
	else if (strlen (argv[1]) >= strlen ("addb")
		 && strncmp (argv[1], "addblocking", strlen (argv[1])) == 0)
	{

		add (IPF_BLOCKING, socket_fd, &argv[2]);

	}
	else if (strlen (argv[1]) >= strlen ("addf")
	       && strncmp (argv[1], "addforwarding", strlen (argv[1])) == 0)
	{

		add (IPF_FORWARDING, socket_fd, &argv[2]);

	}
	else if (strlen (argv[1]) >= strlen ("adda")
	       && strncmp (argv[1], "addaccounting", strlen (argv[1])) == 0)
	{

		add (IPF_ACCOUNTING, socket_fd, &argv[2]);

	}
	else
	{

		fprintf (stderr, "ipfirewall:  unknown command \"%s\"\n", argv[1]);
		show_usage ();
		exit (1);
	}

	exit (0);
}