/*
 *	icmp.c
 */
#include <stdio.h>
#include <string.h>
#include "pcdefs.h"
#include "shared.h"
#include "data.h"
#include "config.h"
#include "funcdef.h"

/****************************************************************************/
/*  icmpinterpret
*   interpret the icmp message that just came in
*/
void
icmpinterpret(pkt, icmplen)
union rawether _far *pkt;
unsigned icmplen;
{
	struct iph _far *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 _far *)
			    ((u_char _far *)&rawicmp(pkt).c.part1 +
			    sizeof(u_long));
			(void) setgate(ip->ipdest,
			    *(u_long _far *)&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 _far *)
		    ((u_char _far *)&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
neticmpsend(to, type, code, buffer, n)
u_long	to;
u_char type;
u_char code;
u_char _far *buffer;
unsigned n;
{
	u_char *pc;
	u_char _far *sp;

	if (n > ICMPMAX)
		n = ICMPMAX;
/*
*  make sure that we have the right dlayer address
*/
	if (to != icmpout->i.ipdest) {
		if (!slip_mode) {
			sp = icmpout->d.dest;
			if (!to || to == Scon.broadcast)
				pc = etherall;
			else {
				pc = netdlayer(to);
				if (!pc)
					return;
			}
			*ipout = blankip;
			_fmemcpy(sp, pc, DADDLEN);
		} else
			*ipout = blankip;
		icmpout->i.ipdest = to;
	}

/*
*  prepare ICMP portion
*/
	icmpout->c.type = type;
 	icmpout->c.code = code;
	if (n) {
		_fmemcpy(icmpout->data, buffer, n);
	}
	n += sizeof(struct icmph);
	icmpout->c.check = 0;
	icmpout->c.check = ipcheck((struct iph _far *)&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));
	n += sizeof (struct ether);
	if (n < 60)
		n = 60;
/*
*  send it
*
*  debug this routine before using
*/
	(void) send_pkt(n);
}

/***************************************************************************/
/*  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
neticmpturn(pkt, len)
union rawether _far *pkt;
unsigned	len;
{
	u_char *pc;

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

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

		_fmemcpy(rawicmp(pkt).d.dest, pc, DADDLEN);
		_fmemcpy(rawicmp(pkt).d.me, 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 _far *)&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
*/
	len += sizeof (struct ether) + sizeof (struct iph);
	_fmemcpy(pkshared->xmitbuf, &rawicmp(pkt), len);
	(void) send_pkt(len);
}
