/*
 * mtutest.c (C) 1995 Darren Reed <avalon@coombs.anu.edu.au>
 *
 * This was written to test what size TCP fragments would get through
 * various TCP/IP packet filters, as used in IP firewalls.  In certain
 * conditions, enough of the TCP header is missing for unpredictable
 * results unless the filter is aware that this can happen.
 *
 * The author provides this program as-is, with no gaurantee for its
 * suitability for any specific purpose.  The author takes no responsibility
 * for the misuse/abuse of this program and provides it for the sole purpose
 * of testing packet filter policies.  This file maybe distributed freely
 * providing it is not modified and that this notice remains in tact.
 *
 * This was written and tested (successfully) on SunOS 4.1.x.
 * To compiile: cc -Bstatic mtutest.c -o mtutest -lkvm
 */
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <kvm.h>
#include <nlist.h>

#define	DESTIP		"127.0.0.1"
#define	DESTPORT	7

struct	nlist	nl[2] = {
	{ "_ifnet" },
	{ NULL }
};

char	opts[8];

struct ifnet *ifp, ifn;
kvm_t	*k;
int mtu;

aclock()
{
	fprintf(stderr, "<alarm>");
	return 0;
}

fixmtu()
{
	ifn.if_mtu = mtu;
	kvm_write(k, ifp, &ifn, sizeof(ifn));
	exit(0);
}

main(argc, argv)
int argc;
char *argv[];
{
	struct	sockaddr_in	sin, loc;
	struct	protoent	*pe;
	struct	ifaddr	ifa;
	char	ifname[16], buf[32], *lif = "le0";
	int	fd, i, opt;

	if (argc > 1)
		lif = argv[1];

	k = kvm_open(NULL, NULL, NULL, O_RDWR, "mtutest");
	kvm_nlist(k, nl);
	signal(SIGALRM, aclock);
	if (kvm_read(k, nl[0].n_value, &ifp, sizeof(ifp)) == -1)
		perror("read");
	while (ifp) {
		if (kvm_read(k, ifp, &ifn, sizeof(ifn)) == -1)
			perror("read");
		if (kvm_read(k, ifn.if_name, ifname, sizeof(ifname)) == -1)
			perror("read");
		sprintf(ifname + strlen(ifname), "%d", ifn.if_unit);
		if (!strcmp(ifname, lif))
			break;
		ifp = ifn.if_next;
	}
	if (!ifp) {
		fprintf(stderr, "couldn't find %s\n", lif);
		exit(1);
	}
	kvm_read(k, ifn.if_addrlist, &ifa, sizeof(ifa));

	signal(SIGINT, fixmtu);
	signal(SIGQUIT, fixmtu);
	signal(SIGHUP, fixmtu);
	mtu = ifn.if_mtu;

	if ((pe = getprotobyname("ip")) == NULL) {
		fprintf(stderr, "ip: unknown protocol\n");
		exit(1);
	}

	bcopy((char *)&ifa.ifa_addr, (char *)&loc, sizeof(loc));
	bzero((char *)loc.sin_zero, sizeof(loc.sin_zero));
	loc.sin_family = AF_INET;
	loc.sin_port = 0;

	opts[IPOPT_OPTVAL] = IPOPT_NOP;
	opts[IPOPT_OLEN] = 0;
	opts[IPOPT_OFFSET] = 0;

	printf("name %s\tip %s\tmetric %d\n", ifname, inet_ntoa(loc.sin_addr),
		ifn.if_mtu);
	printf("minlen: ip %d + tcp %d = %d\n", sizeof(struct ip),
		sizeof(struct tcphdr),
		sizeof(struct ip) + sizeof(struct tcphdr));

	for (i = 20; i < 128; i += 2) {
		ifn.if_mtu = i;
		kvm_write(k, ifp, &ifn, sizeof(ifn));
		printf("mtu %d - ", i);
		fflush(stdout);
		fd = socket(AF_INET, SOCK_STREAM, 0);
		opt = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
				sizeof(opt)) == -1)
			perror("setsockopt");
/*		if (setsockopt(fd, pe->p_proto, IP_OPTIONS, opts, 4) == -1)
			perror("setsockopt");*/
		loc.sin_port = htons(0);
		bind(fd, &loc, sizeof(loc));
		bzero((char *)&sin, sizeof(sin));
		sin.sin_family = AF_INET;
		sin.sin_port = htons(DESTPORT);
		sin.sin_addr.s_addr = inet_addr(DESTIP);
		alarm(5);
		if (connect(fd, &sin, sizeof(sin)) == -1) {
			if (errno != EINTR)
				perror("connect");
			else
				printf("\n");
		} else
			printf("connected\n");
		alarm(0);
		close(fd);
		sleep(1);
	}
	fixmtu();
	/*NOT REACHED*/
}
