/*
 *	ip.c
 */
#include "pcdefs.h"
#include "protocol.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 _fastcall
ipinterpret(ptr, len)
union rawether *ptr;
u_int	len;
{
	u_int	iplen;
	u_int	i;
	struct pseudotcp itcps;

/*
*  We cannot handle fragmented IP packets yet, return an error
*/
	if (ptr->ip.i.frags & 0x20)
		return;
/*
*  checksum verification of IP header
*/
	i = (ptr->ip.i.versionandhdrlen & 0x0f) << 2;
	if (i < sizeof (struct iph))	/* bad ip header*/
		return;
	iplen = ntohs(ptr->ip.i.tlen);
	if (len < iplen + sizeof (DLAYER) || iplen < i) {
		return;
	}

	if (ptr->ip.i.check != 0) {
		if (ipcheck(&ptr->ip.i, i) != 0xffff)
			return; 	/* drop packet */
	}
	/* If this packet has IP options, throw them away now */
	if (i > sizeof (struct iph)) {
		struct iph *oldip = &ptr->ip.i;
		ptr = (union rawether *)((u_char *)ptr +
		    (i - sizeof (struct iph)));
		ptr->ip.i = *oldip;
	}
/*
*  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 != ptr->ip.i.ipdest) {
		/* potential non-match */
		if (Scon.broadcast != ptr->ip.i.ipdest) {
			if (0L == Scon.myip && Scon.flags & H_BOOTP &&
			    ptr->ip.i.protocol == PROTUDP &&
			    ptr->udp.u.dest == bpstat.listen) {
				(void) parse_bootp(
				    (struct bootp *)ptr->udp.data);
			}
			return;
		}
	}

	/* We already threw away the options. */
	iplen -= sizeof (struct iph);
	itcps.tcplen = htons(iplen);
	itcps.z = 0;
	memcpy(&itcps, &ptr->udp.i.ipsource, 2 * sizeof(u_long));

	switch (itcps.proto = ptr->ip.i.protocol) {
	    /* which protocol to handle this packet? */
	    case PROTTCP:
		if (ptr->tcp.t.check != 0) {
			if (tcpcheck(&itcps, &ptr->tcp.t, iplen) != 0xffff)
				break;
		}
		tcpinterpret(ptr, iplen);
		break;
	    case PROTUDP:
		if (ptr->udp.u.check != 0) {
		    if (tcpcheck(&itcps,
			(struct tcph _far *)&ptr->udp.u, iplen) != 0xffff)
			    break;
		}
		udpinterpret(ptr, iplen - sizeof(struct udph));
		break;
	    case PROTICMP:
		if (ptr->icmp.c.check != 0) {
		    if (ipcheck((struct iph *)&ptr->icmp.c, iplen) != 0xffff)
			break;
		}
		icmpinterpret(ptr, iplen);
		break;
	    default:
		/* protocol unreachable */
		if (Scon.myip == ptr->ip.i.ipdest)
		    neticmpsend(ptr->ip.i.ipsource, 3, 2,
			(u_char *)&ptr->ip.i, sizeof(struct iph) +
			2 * sizeof(u_long));
		break;
	}
}
