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

/****************************************************************************/
/*  icmpinterpret
*   interpret the icmp message that just came in
*/
void _near _fastcall
icmpinterpret(pkt, icmplen)
union rawether near *pkt;
u_int icmplen;
{
	u_int	i;
	struct iph near *ip;

	switch (rawicmp(pkt).c.type) {
	    case 3:
dfputs("icmp: dest unreachable\r\n");
		break;
	    case 5:				/* ICMP redirect */
		switch(rawicmp(pkt).c.code) {
		    case 0:
dfputs("icmp network redirect\r\n");
			ip = (struct iph near *)
			    ((u_char near *)&rawicmp(pkt).c.part1 +
			    sizeof(u_long));
			(void) setgate(ip->ipdest,
			    *(u_long *)&rawicmp(pkt).c.part1);
			break;
		    case 1:
dfputs("icmp host redirect\r\n");
			break;
		    case 2:
dfputs("icmp TOS & network redirect\r\n");
			break;
		    case 3:
dfputs("icmp TOS & host redirect\r\n");
			break;
		}
		break;
	    case 8:				/* ping request sent to me */
		(void) neticmpturn(pkt, icmplen);	/* send back */
		break;
	    case INMREP:
		Scon.snetmask = *(u_long near *)
		    ((u_char near *)&rawicmp(pkt).c.part1 + 4);
		break;
	    default:
dfputs("icmp: unknown type\r\n");
	}
	return;
}

/***************************************************************************/
/*  neticmpsend
*
*   send out an icmp packet, probably to do a ping operation
*	machine = ip address of destination
*	type = icmp type value
*	code = icmp code value
*	buffer = data to be copied in to the icmp packet
*	n = number of bytes to copy
*/
void near
neticmpsend(to, type, code, buffer, n)
u_long	to;
u_char type;
u_char code;
u_char near *buffer;
u_int n;
{
	u_char near *pc;

	if (n > ICMPMAX)
		n = ICMPMAX;
/*
*  make sure that we have the right dlayer address
*/
	if (to != icmpout.i.ipdest) {
	    if (!slip_mode) {
		if (to == ipall)
{
dfputs("icmpsend:setting dest to broadcast\r\n");
			pc = etherall;
}
		else {
dfputs("icmpsend:looking for machine\r\n");
			pc = netdlayer(to);
			if (!pc)
				return;
		}
		(void) memcpy((u_char near *)icmpout.d.dest, (u_char near *)pc,
		    DADDLEN);
	    }
	    icmpout.i.ipdest = to;
	}

/*
*  prepare ICMP portion
*/
	icmpout.c.type = type;
 	icmpout.c.code = code;
	if (n) {
		(void) memcpy((u_char near *)icmpout.data,
		    (u_char near *)buffer, n);
	}
	n += sizeof(struct icmph);
	icmpout.c.check = 0;
	icmpout.c.check = ipcheck((struct iph near *)&icmpout.c, n);

/*
*   iplayer for send
*/
	icmpout.i.protocol = PROTICMP;
	n += sizeof(struct iph);
	icmpout.i.tlen = htons(n);
	icmpout.i.ident = htons(nnipident);
	nnipident++;
	icmpout.i.check = 0;
	icmpout.i.check = ipcheck(&icmpout.i, sizeof(struct iph));
/*
*  send it
*
*  debug this routine before using
*/
	(void) pkxmit((u_char far *)&icmpout, n + sizeof(struct ether));
}

/***************************************************************************/
/*  neticmpturn
*
*   send out an icmp packet, probably in response to a ping operation
*   interchanges the source and destination addresses of the packet,
*   puts in my addresses for the source and sends it
*
*   does not change any of the ICMP fields, just the IP and dlayers
*/
void near
neticmpturn(pkt, len)
union rawether near *pkt;
u_int	len;
{
	u_char near *pc;

/*
*  reverse the addresses, dlayer and IP layer
*/
	if (!slip_mode) {
	    if (!memcmp((u_char near *)rawicmp(pkt).d.me,
		(u_char near *)etherall, DADDLEN))
		    return;

	    pc = cachelook(find_route(rawicmp(pkt).i.ipsource));
	    if (!pc)
		    return;

	    (void) memcpy((u_char near *)rawicmp(pkt).d.dest,
		(u_char near *)pc, DADDLEN);
	    (void) memcpy((u_char near *)rawicmp(pkt).d.me,
		(u_char near *)myether, DADDLEN);
	}
	rawicmp(pkt).i.ipdest = rawicmp(pkt).i.ipsource;
	rawicmp(pkt).i.ipsource = Scon.myip;
/*
*  prepare ICMP checksum
*/
	rawicmp(pkt).c.type = 0;	/* echo reply type */
	rawicmp(pkt).c.check = 0;
	rawicmp(pkt).c.check = ipcheck((struct iph near *)&rawicmp(pkt).c,
	    len);
/*
*   iplayer for send
*/
	rawicmp(pkt).i.ident = htons(nnipident);
	nnipident++;
	rawicmp(pkt).i.check = 0;
	rawicmp(pkt).i.check = ipcheck(&rawicmp(pkt).i, sizeof(struct iph));
/*
*  send it
*/
	(void) pkxmit((u_char far *)&rawicmp(pkt),
	    len + sizeof(struct ether) + sizeof(struct iph));
}
