/* ICMP header conversion routines
 * Copyright 1991 Phil Karn, KA9Q
 */
#include "global.h"
#include "mbuf.h"
#include "internet.h"
#include "ip.h"
#include "icmp.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: icmphdr.c,v 1.7 1996/08/29 12:11:16 root Exp $";
#endif


/* Generate ICMP header in network byte order, link data, compute checksum */
struct mbuf *
htonicmp (icmp, data)
struct icmp *icmp;
struct mbuf *data;
{
struct mbuf *bp;
register unsigned char *cp;
int16 thechecksum;

	if ((bp = pushdown (data, ICMPLEN)) == NULLBUF)
		return NULLBUF;
	cp = bp->data;

	*cp++ = uchar(icmp->type);
	*cp++ = uchar(icmp->code);
	cp = put16 (cp, 0);	/* Clear checksum */
	switch (icmp->type) {
		case ICMP_DEST_UNREACH:
			if (icmp->code == ICMP_FRAG_NEEDED) {
				/* Deering/Mogul max MTU indication */
				cp = put16 (cp, 0);
				cp = put16 (cp, icmp->args.mtu);
			} else
				cp = put32 (cp, 0L);
			break;
		case ICMP_PARAM_PROB:
			*cp++ = icmp->args.pointer;
			*cp++ = 0;
			cp = put16 (cp, 0);
			break;
		case ICMP_REDIRECT:
			cp = put32 (cp, icmp->args.address);
			break;
		case ICMP_ECHO:
		case ICMP_ECHO_REPLY:
		case ICMP_TIMESTAMP:
		case ICMP_TIME_REPLY:
		case ICMP_INFO_RQST:
		case ICMP_INFO_REPLY:
			cp = put16 (cp, icmp->args.echo.id);
			cp = put16 (cp, icmp->args.echo.seq);
			break;
		default:
			cp = put32 (cp, 0L);
			break;
	}
	/* Compute checksum, and stash result */
	thechecksum = cksum (NULLHEADER, bp, len_p (bp));
	cp = &bp->data[2];
	cp = put16 (cp, thechecksum);

	return bp;
}


/* Pull off ICMP header */
int
ntohicmp (icmp, bpp)
struct icmp *icmp;
struct mbuf **bpp;
{
char icmpbuf[8];

	if (icmp == (struct icmp *) NULL)
		return -1;
	if (pullup (bpp, (unsigned char *) icmpbuf, 8) != 8)
		return -1;
	icmp->type = icmpbuf[0];
	icmp->code = icmpbuf[1];
	switch (icmp->type) {
		case ICMP_DEST_UNREACH:
			/* Retrieve Deering/Mogul MTU value */
			if (icmp->code == ICMP_FRAG_NEEDED)
				icmp->args.mtu = get16 (&icmpbuf[6]);
			break;
		case ICMP_PARAM_PROB:
			icmp->args.pointer = uchar (icmpbuf[4]);
			break;
		case ICMP_REDIRECT:
			icmp->args.address = get32 (&icmpbuf[4]);
			break;
		case ICMP_ECHO:
		case ICMP_ECHO_REPLY:
		case ICMP_TIMESTAMP:
		case ICMP_TIME_REPLY:
		case ICMP_INFO_RQST:
		case ICMP_INFO_REPLY:
			icmp->args.echo.id = get16 (&icmpbuf[4]);
			icmp->args.echo.seq = get16 (&icmpbuf[6]);
			break;
		default:
			break;
	}
	return 0;
}
