/*
 * Micro-X -- an X server for DOS
 * Copyright (C) 1992 StarNet Communications Corp.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * StarNet Communications Corp.
 * 550 Lakeside Dr. #3
 * Sunnyvale CA 94086 US
 * http://www.starnet.com
 * x-dos@starnet.com
 */
/*
 *	icmp.c
 */
#include "pcdefs.h"
#include "protocol.h"
#include "config.h"
#include "funcdef.h"

struct icmp icmpout;
static void neticmpturn(union rawether *, u_int);
extern u_char etherall[DADDLEN];
extern u_char myether[DADDLEN];
extern char slip_mode;
extern u_short nnipident;

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

	switch (rawicmp(pkt).c.type) {
	    case 3:
		break;
	    case 5:				/* ICMP redirect */
		switch(rawicmp(pkt).c.code) {
		    case 0:
			ip = (struct iph *)
			    ((u_char *)&rawicmp(pkt).c.part1 + sizeof(u_long));
			setgate(ip->ipdest,
			    *(u_long _far *)&rawicmp(pkt).c.part1);
			break;
		    case 1:
			break;
		    case 2:
			break;
		    case 3:
			break;
		}
		break;
	    case 8:				/* ping request sent to me */
		(void) neticmpturn(pkt, icmplen);	/* send back */
		break;
	    case INMREP:
		Scon.snetmask = *(u_long *)
		    ((u_char *)&rawicmp(pkt).c.part1 + 4);
		break;
	    default:
		break;
	}
}

/***************************************************************************/
/*  neticmpsend
*
*   send out an icmp packet, probably to send a port unreachable
*	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;
void _far *buffer;
u_int n;
{
	u_char *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 || to == Scon.broadcast)
				pc = etherall;
			else {
				to = find_route(to);
				if (!to)
					return;		/* no route */
				pc = netdlayer(to);
				if (!pc)
					return;
			}
			memcpy(icmpout.d.dest, pc, DADDLEN);
		}
		icmpout.i.ipdest = to;
	}

/*
*  prepare ICMP portion
*/
	icmpout.c.type = type;
 	icmpout.c.code = code;
	if (n) {
		memcpy(icmpout.data, buffer, n);
	}
	n += sizeof(struct icmph);
	icmpout.c.check = 0;
	icmpout.c.check = ipcheck((struct iph *)&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);
	pkxmit(&icmpout, 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
*/
static void
neticmpturn(pkt, len)
union rawether *pkt;
u_int	len;
{
	u_char *pc;

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

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

		memcpy(rawicmp(pkt).d.dest, pc, DADDLEN);
		memcpy(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 *)&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);
	(void)pkxmit(&rawicmp(pkt), len);
}
