/*
 *	ip.c
 */
#include "pcdefs.h"
#include "mbuf.h"
#include "protocol.h"
#include "data.h"
#include "config.h"
#include "funcdef.h"

/***************************************************************************/
/*  ipinterpret
*   Called by the reception routine with a new IP packet.  Check the checksum,
*   addressing and protocol type and call appropriate routines.
*/
void _near _fastcall
ipinterpret(ptr, len)
union rawether near *ptr;
u_int	len;
{
	u_int	iplen;
	u_int	i;

	if (slip_mode) {
		/* Warning: ptr points to 14 bytes of memory that can't
		 * be overwritten!
		 */
		ptr = (union rawether near *)((u_char near *)ptr -
		    sizeof (struct ether));
	/*	len += sizeof (struct ether); len is ignored anyway */
	}
/*
*  We cannot handle fragmented IP packets yet, return an error
*/
	if (rawip(ptr).i.frags & 0x20) {
dfputs("f");
		return;
	}
/*
*  checksum verification of IP header
*/
	i = (rawip(ptr).i.versionandhdrlen & 0x0f) << 2;
	if (i < sizeof(struct iph))	/* bad ip header*/
		return;

	if (iplen = rawip(ptr).i.check) {
		rawip(ptr).i.check = 0;
		if (iplen != ipcheck(&rawip(ptr).i, i))
			return; 	/* drop packet */
	}
	if (i > sizeof(struct iph)) {
dfputs("got ip options\r\n");
		memcpy(&rawip(ptr).i, (char near *)&rawip(ptr).i +
		    (i - sizeof (struct iph)), sizeof (struct iph));
		ptr += i - sizeof (struct iph);
	}
/*
*  check to make sure that the packet is for me.
*  Throws out all packets which are not directed to my IP address.
*  Nor to the IP broadcast address.
*/
	if (Scon.myip != rawip(ptr).i.ipdest) {
		/* potential non-match */
		if (ipall != rawip(ptr).i.ipdest) {
			if (0L == Scon.myip && Scon.flags & H_BOOTP &&
			    rawip(ptr).i.protocol == PROTUDP &&
			    rawudp(ptr).u.dest == bpstat.listen) {
				(void) parse_bootp(
				    (struct bootp near *)rawudp(ptr).data);
			}
dfputs("i");
			return;
		}
	}

	iplen = ntohs(rawip(ptr).i.tlen) - i;
	itcps.tcplen = htons(iplen);
	(void) memcpy((u_char near *)&itcps,
	    (u_char near *)&rawudp(ptr).i.ipsource, 2 * sizeof(u_long));

	switch (itcps.proto = rawip(ptr).i.protocol) {
	    /* which protocol to handle this packet? */
	    case PROTTCP:
		if (i = rawtcp(ptr).t.check) {
			rawtcp(ptr).t.check = 0;
			if (i != tcpcheck((struct pseudotcp far *)&itcps,
			    (struct tcph far *)&rawtcp(ptr).t, iplen))
				break;
		}
		(void) tcpinterpret(ptr, iplen);
		break;
	    case PROTUDP:
		if (i = rawudp(ptr).u.check) {
			rawudp(ptr).u.check = 0;
			if (i != tcpcheck((struct pseudotcp far *)&itcps,
			    (struct tcph far *)&rawudp(ptr).u, iplen))
				break;
		}
		(void) udpinterpret(ptr, iplen - sizeof(struct udph));
		break;
	    case PROTICMP:
		if (i = rawicmp(ptr).c.check) {
			rawicmp(ptr).c.check = 0;
			if (i != ipcheck((struct iph near *)&rawicmp(ptr).c,
			     iplen))
				break;
		}
		(void) icmpinterpret(ptr, iplen);
		break;
	    default:
		/* protocol unreachable */
		(void) neticmpsend(rawip(ptr).i.ipsource, 3, 2,
		    (u_char near *)&rawip(ptr).i, sizeof(struct iph) +
		    2 * sizeof(u_long));
		break;
	}
}
