/*
 *	ip.c
 */
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include "pcdefs.h"
#include "mbuf.h"
#include "protocol.h"
#include "data.h"
#include "config.h"
#include "funcdef.h"

u_short nnipident;
extern struct bpport bpstat;

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

	len = len;	/* not used */
/*
printf("ipinterpret %#x:%#x, len %#x proto %d\n", FP_SEG(ptr), FP_OFF(ptr),
	len, rawip(ptr).i.protocol);
*/

/*
*  We cannot handle fragmented IP packets yet, return an error
*/
	if (rawip(ptr).i.frags & 0x20)
		return;
/*
*  checksum verification of IP header
*/
	i = (rawip(ptr).i.versionandhdrlen & 0x0f) << 2;
	if (i < sizeof (struct iph))	/* bad ip header*/
		return;
	/* If this packet has IP options, throw them away now */
	if (i > sizeof (struct iph)) {
		struct iph _far *oldip = &rawip(ptr).i;
		ptr = (union rawether _far *)((u_char _far *)ptr +
		    (i - sizeof (struct iph)));
		rawip(ptr).i = *oldip;
	}

	if ((iplen = rawip(ptr).i.check) != 0) {
		rawip(ptr).i.check = 0;
		if (iplen != ipcheck(&rawip(ptr).i, i))
			return; 	/* drop packet */
	}
/*
*  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 (Scon.broadcast != 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 _far *)rawudp(ptr).data);
			}
			return;
		}
	}

	iplen = ntohs(rawip(ptr).i.tlen) - i;
	itcps.tcplen = htons(iplen);
	_fmemcpy(&itcps, &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) != 0) {
			rawtcp(ptr).t.check = 0;
			if (i != tcpcheck(&itcps,
			    (struct tcph _far *)&rawtcp(ptr).t, iplen))
				break;
		}
		tcpinterpret(ptr, iplen);
		break;
	    case PROTUDP:
		if ((i = rawudp(ptr).u.check) != 0) {
			rawudp(ptr).u.check = 0;
			if (i != tcpcheck(&itcps,
			    (struct tcph _far *)&rawudp(ptr).u, iplen))
				break;
		}
		udpinterpret(ptr, iplen - sizeof(struct udph));
		break;
	    case PROTICMP:
		if ((i = rawicmp(ptr).c.check) != 0) {
			rawicmp(ptr).c.check = 0;
			if (i != ipcheck((struct iph _far *)&rawicmp(ptr).c,
			     iplen))
				break;
		}
		(void) icmpinterpret(ptr, iplen);
		break;
	    default:
		/* protocol unreachable */
		(void) neticmpsend(rawip(ptr).i.ipsource, 3, 2,
		    (u_char _far *)&rawip(ptr).i, sizeof(struct iph) +
		    2 * sizeof(u_long));
		break;
	}
}
