/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <unistd.h>
# include <fcntl.h>
# include <sys/types.h>
# include <sys/time.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <sys/select.h>
# include <net/if.h>
# ifdef HAVE_NET_IF_VAR_H
#  include <net/if_var.h>
# endif
# ifdef HAVE_NET_IF_TUN_H
#  include <net/if_tun.h>
# endif
# ifdef HAVE_LINUX_IF_TUN_H
#  include <linux/if_tun.h>
# endif
# ifdef TUNNEWPPA
#  include <ctype.h>
#  include <stropts.h>
#  include <sys/stream.h>
#  include <sys/sockio.h>
#  include <sys/dlpi.h>
# endif
# include "epp.h"
# include "../libutil/util.h"

RCSID("$Id: epp_tun.c 466 2001-03-01 10:46:44Z hbb $")

int	verbose;
char	*ifname;
u_char	my_ether[6];
u_char	other_ether[6];

void init_tun(void);
void dump_packet(u_char *, u_int);

int
main(int argc, char *argv[])
{
	int opt;

	set_argv0(progname = argv[0]);
	while((opt = getopt(argc, argv, "v")) != EOF)
		switch(opt) {

		  case 'v':
			verb_option("all");
			break;
		}

	argc -= optind;
	argv += optind;

	if(argc != 3)
		panic("need one arg");

	ifname = argv[0];
	parse_ether(my_ether, argv[1]);
	parse_ether(other_ether, argv[2]);

	init_tun();
	init_sigs();
	open_shmem();

	loop();
}

void
init_tun()
{
# ifndef TUNNEWPPA
	if((fd = open(ifname, O_RDWR, 0)) < 0)
		panic("%s: %s", ifname, strerror(errno));

	verb(V_DFLT, 1, "open %s on fd=%d\n", ifname, fd);
# endif

# ifdef TUNGIFINFO
	{
		struct tuninfo tuninfo;
		if(ioctl(fd, TUNGIFINFO, &tuninfo))
			panic("TUNGIFINFO: %s", strerror(errno));

		verb(V_DFLT, 1, "old.baudrate=%d\n", tuninfo.baudrate);
		verb(V_DFLT, 1, "old.mtu=%d\n", tuninfo.mtu);
		verb(V_DFLT, 1, "old.type=%u\n", tuninfo.type);

		tuninfo.baudrate = 0;
		tuninfo.type = 0;
		tuninfo.mtu = ETHMTU - 14;	/* XXX */

		if(ioctl(fd, TUNSIFINFO, &tuninfo))
			panic("TUNSIFINFO: %s", strerror(errno));

		if(ioctl(fd, TUNGIFINFO, &tuninfo))
			panic("TUNGIFINFO: %s", strerror(errno));

		verb(V_DFLT, 1, "new.baudrate=%d\n", tuninfo.baudrate);
		verb(V_DFLT, 1, "new.mtu=%d\n", tuninfo.mtu);
		verb(V_DFLT, 1, "new.type=%u\n", tuninfo.type);
	}
# endif

# ifdef TUNSETIFF
	{
		struct ifreq ifr;

		memset(&ifr, 0, sizeof(ifr));
		ifr.ifr_flags = IFF_TUN | IFF_NO_PI;

		if(ioctl(fd, TUNSETIFF, (void *)&ifr) < 0)
			panic("TUNSETIFF: %s", strerror(errno));
		ifname = xstrsave(ifr.ifr_name);
	}
# endif

# ifdef TUNNEWPPA
	int ppa;
	char *ptr;

	ptr = ifname + strlen(ifname);
	while(ptr > ifname && isascii(ptr[-1]) && isdigit((int)ptr[-1]))
		ptr--;
	ppa = atoi(ptr);
	*ptr = '\0';

	if((fd = open(ifname, O_RDWR, 0)) < 0)
		panic("open(%s): %s", ifname, strerror(errno));
	verb(V_DFLT, 1, "open %s on fd=%d\n", ifname, fd);

	if((ppa = ioctl(fd, TUNNEWPPA, ppa)) < 0)
		panic("tunnewppa: %s", strerror(errno));
	verb(V_DFLT, 1, "%s: ppa=%d", ifname, ppa);
# endif
}

/*
 * Filter an input frame. We assume, that we only get packets, we should
 * really get. This means, there is no sense to filter.
 */
int
input_filter(u_char *buf UNUSED)
{
	return 1;
}

/*
 * All filtering should be done in the IP layers on both sides,
 * so we do no filtering altogether.
 */
void
set_filter()
{
}

/*
 * Transmit a frame. Do this only, if it is an IP packet.
 */
void
transmit()
{
	u_short proto;
	int ret;

	if(xlen < 14) {
		verb(V_DFLT, 1, "frame too short: %d", xlen);
		return;
	}
	if(verbose > 1)
		dump_packet(xbuf, xlen);
	proto = (xbuf[12] << 8) | xbuf[13];
	if(proto != 0x0800) {
		verb(V_DFLT, 1, "non-ip packet %04x", proto);
		return;
	}
	if((ret = write(fd, xbuf+14, xlen-14)) < 0)
		panic("write: %s", strerror(errno));
	if((u_int)ret != xlen - 14)
		panic("write: bad %d %u", ret, xlen - 14);
}

u_int
read_input(u_char **pbuf, int *more)
{
	static char	rbuf[2048+14];
	int len;

	*more = 0;
	memcpy(rbuf, my_ether, 6);
	memcpy(rbuf + 6, other_ether, 6);
	rbuf[12] = 0x08;
	rbuf[13] = 0x00;
	if((len = read(fd, rbuf+14, sizeof(rbuf)-14)) < 0)
		panic("tun: %s", strerror(errno));
	if(len == 0)
		panic("tun: EOF");
	*pbuf = rbuf;
	if(verbose > 1)
		dump_packet(rbuf, len + 14);
	return len + 14;
}

/*
 * Generate local info string.
 */
# define P	buf+strlen(buf)
void
do_info(char *buf)
{
	sprintf(P, "epp_tun ifname=%s fd=%d", ifname, fd);
}

/*
 * Reset of QNA.
 */
void
reset()
{
}

/*
 * Change receiver state.
 */
void
rcv_enable_change()
{
}


void
dump_packet(u_char *p, u_int len)
{
	fprintf(stderr, "%02x:%02x:%02x:%02x:%02x:%02x ",
		p[0], p[1], p[2], p[3], p[4], p[5]);
	p += 6; len -= 6;
	fprintf(stderr, "%02x:%02x:%02x:%02x:%02x:%02x ",
		p[0], p[1], p[2], p[3], p[4], p[5]);
	p += 6; len -= 6;
	fprintf(stderr, "%04x", (p[0] << 8) | p[1]);
	if(((p[0] << 8) | p[1]) != 0x0800) {
		fprintf(stderr, "\n");
		return;
	}
	p += 2; len -= 2;
	if(len < 20) {
		fprintf(stderr, " too short!\n");
		return;
	}
	fprintf(stderr, " v=%x", (p[0] >> 4) & 0xf);
	if((p[0] & 0xf0) != 0x40) {
		fprintf(stderr, "!\n");
		return;
	}
	fprintf(stderr, " hl=%d", p[0] & 0xf);
	fprintf(stderr, "\n");
}
